'use server';

import bcrypt from 'bcrypt';
import { revalidatePath } from 'next/cache';
import { prisma } from 'src/lib/prisma';
import { loadSession } from 'src/lib/session';
import { v4 as uuid } from 'uuid';

import { z } from 'zod';

const SignupFormSchema = z.object({
  name: z.string().min(2, { message: '用户名必须至少包含2个字符。' }).trim(),
  email: z.string().email({ message: '请输入有效的电子邮箱地址。' }).trim(),
  password: z
    .string()
    .min(8, { message: '至少8个字符长度' })
    .regex(/[a-zA-Z]/, { message: '至少包含一个字母。' })
    .regex(/[0-9]/, { message: '至少包含一个数字。' })
    .regex(/[^a-zA-Z0-9]/, {
      message: '至少包含一个特殊字符。',
    })
    .trim(),
});

const SignInFormSchema = z.object({
  email: z.string().email({ message: '请输入有效的电子邮箱地址。' }).trim(),
  password: z.string().min(1, { message: '密码不能为空' }).trim(),
});

interface FormState<ValidatedFields> {
  actionId: string;
  status: 'success' | 'error';
  errors?: {
    // 表单验证错误信息
    form?: ValidatedFields;
    // 系统提示错误信息，例如：网络错误、服务器错误等
    message?: string;
  };
}

type SignupFormState =
  | FormState<{
      name?: string[];
      email?: string[];
      password?: string[];
    }>
  | undefined;

type SignInFormState =
  | FormState<{
      email?: string[];
      password?: string[];
    }>
  | undefined;

export async function signup(
  previousState: SignupFormState,
  formData: FormData
): Promise<SignupFormState> {
  const actionId = uuid(); // Generate a unique ID for the action
  try {
    // Validate form fields
    const validatedFields = SignupFormSchema.safeParse({
      name: formData.get('name'),
      email: formData.get('email'),
      password: formData.get('password'),
    });

    // If any form fields are invalid, return early
    if (!validatedFields.success) {
      return {
        actionId,
        status: 'error',
        errors: {
          form: validatedFields.error.flatten().fieldErrors,
          message: '表单验证失败，请检查输入。',
        },
      };
    }

    // Call the provider or db to create a user...
    // 2. Prepare data for insertion into database
    const { name, email, password } = validatedFields.data;
    // e.g. Hash the user's password before storing it
    const hashedPassword = await bcrypt.hash(password, 10);
    // 3. Check if the email already exists in the database
    const existingEmail = await prisma.user.findUnique({
      where: {
        email,
      },
    });
    // If email already exists, return error
    if (existingEmail) {
      return {
        actionId,
        status: 'error',
        errors: {
          form: {
            email: ['该邮箱已被注册，请使用其他电子邮件地址。'],
          },
          message: '注册失败：该电子邮件地址已被注册！',
        },
      };
    }
    // 4. Insert the user into the database or call an Auth Library's API
    await prisma.user.create({
      data: {
        name,
        email,
        password: hashedPassword,
      },
    });
    // 4. If everything is valid, return success
    return {
      actionId,
      status: 'success',
    };
  } catch (error) {
    console.error('Error during signup:', error);
    const errorMessage = `服务器错误：${(error as Error)?.message || 'An error occurred during signup.'}`;
    return {
      actionId,
      status: 'error',
      errors: {
        message: errorMessage,
      },
    };
  }
}

export async function login(
  previousState: SignInFormState,
  formData: FormData
): Promise<SignInFormState> {
  const actionId = uuid(); // Generate a unique ID for the action
  try {
    // Validate form fields
    const validatedFields = SignInFormSchema.safeParse({
      email: formData.get('email'),
      password: formData.get('password'),
    });

    // If any form fields are invalid, return early
    if (!validatedFields.success) {
      return {
        actionId,
        status: 'error',
        errors: {
          form: validatedFields.error.flatten().fieldErrors,
          message: '表单验证失败，请检查输入。',
        },
      };
    }
    // 2. Prepare data for insertion into database
    const { email, password } = validatedFields.data;
    // 3. Check if the user exists in the database
    const user = await prisma.user.findUnique({
      where: {
        email,
      },
    });
    // If user not found, return error
    if (!user) {
      return {
        actionId,
        status: 'error',
        errors: {
          form: {
            email: ['该邮箱未注册，请检查您的电子邮件地址。'],
          },
          message: '登陆失败，用户不存在！',
        },
      };
    }
    // 4. Compare the provided password with the stored hashed password
    const isPasswordValid = await bcrypt.compare(password, user.password);
    // If password is invalid, return error
    if (!isPasswordValid) {
      return {
        actionId,
        status: 'error',
        errors: {
          form: {
            password: ['密码错误，请检查您的密码。'],
          },
          message: '登录失败，密码错误！',
        },
      };
    }
    // 5. If everything is valid, return success
    // clear all cache
    revalidatePath('/', 'layout');
    // NOTE: 如有必要：清除客户端缓存，在客户端重新渲染页面。 `router.refresh();`
    // set session
    const session = await loadSession();
    session.isLoggedIn = true;
    session.username = user.name;
    session.userId = user.id;
    session.role = user.role;
    await session.save();
    return {
      actionId,
      status: 'success',
    };
  } catch (error) {
    console.error('Error during signin:', error);
    const errorMessage = `服务器错误：${(error as Error)?.message || 'An error occurred during signin.'}`;
    return {
      actionId,
      status: 'error',
      errors: {
        message: errorMessage,
      },
    };
  }
}

export async function logout() {
  const session = await loadSession();
  session.destroy();
  // 清除服务端缓存
  revalidatePath('/', 'layout');
  // NOTE: 如有必要：清除客户端缓存，在客户端重新渲染页面。 `router.refresh();`
}

/**
 * 获取当前用户信息
 * @description 如果没有传入 userId，则从 session 中获取用户信息
 * @param userId - 用户 ID
 */
export async function getUser(userId?: string) {
  // 如果传入了 userId，则直接查询用户
  if (userId) {
    const user = await prisma.user.findUnique({
      where: {
        id: userId,
      },
    });
    return user;
  }

  // 如果没有传入 userId，则从 session 中获取用户信息
  const session = await loadSession();
  if (session.userId) {
    const user = await prisma.user.findUnique({
      where: {
        id: session.userId,
      },
    });
    return user;
  }

  // 如果没有userId，则返回 null
  return null;
}
